﻿using System;
using System.Collections.Generic;
using NHibernate.Mapping.ByCode;
using Xenta.Enums;
using Xenta.Utils;

namespace Xenta.Entities
{
    /// <summary>
    /// Exposes the address interface.
    /// </summary>
    public interface IAddress
    {
        #region Properties

        /// <summary>
        /// Gets or sets the country.
        /// </summary>
        CountryEntity Country
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the region.
        /// </summary>
        RegionEntity Region
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the city.
        /// </summary>
        string City
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the address 1.
        /// </summary>
        string Address1
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the address 2.
        /// </summary>
        string Address2
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the postal code.
        /// </summary>
        string PostalCode
        {
            get;
            set;
        }

        #endregion
    }

    /// <summary>
    /// Exposes the contact interface.
    /// </summary>
    public interface IContact
    {
        #region Properties

        /// <summary>
        /// Gets or sets the contact name.
        /// </summary>
        string ContactName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the postal code.
        /// </summary>
        string PostalCode
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the phone number.
        /// </summary>
        string PhoneNumber
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the fax number.
        /// </summary>
        string FaxNumber
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the email.
        /// </summary>
        string Email
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the website.
        /// </summary>
        string Website
        {
            get;
            set;
        }

        #endregion
    }

    /// <summary>
    /// Represents the address entity base.
    /// </summary>
    /// <remarks>
    /// To define the concrete address, please use a string 
    /// based discriminator up to 10 characters.
    /// </remarks>
    public abstract class AddressEntity : AuditableEntity, 
        IAddress, IContact
    {
        #region Properties

        /// <summary>
        /// Gets or sets the holder identifier.
        /// </summary>
        public virtual int HolderID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the country.
        /// </summary>
        public virtual CountryEntity Country
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the region.
        /// </summary>
        public virtual RegionEntity Region
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the city.
        /// </summary>
        public virtual string City
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the address 1.
        /// </summary>
        public virtual string Address1
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the address 2.
        /// </summary>
        public virtual string Address2
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the entity name.
        /// </summary>
        public virtual string EntityName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the contact name.
        /// </summary>
        public virtual string ContactName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the postal code.
        /// </summary>
        public virtual string PostalCode
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the phone number.
        /// </summary>
        public virtual string PhoneNumber
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the fax number.
        /// </summary>
        public virtual string FaxNumber
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the email.
        /// </summary>
        public virtual string Email
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the website.
        /// </summary>
        public virtual string Website
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags value.
        /// </summary>
        public virtual int FlagsValue
        {
            get;
            set;
        }

        #endregion

        #region Custom Properties

        /// <summary>
        /// Gets the address short form.
        /// </summary>
        public virtual string ShortForm
        {
            get
            {
                return GeoHelper.AddressShortForm(
                    !String.IsNullOrWhiteSpace(EntityName) 
                        ? String.Format("{0} / {1}", ContactName, EntityName)
                        : ContactName,
                    Address1,
                    Address2,
                    City,
                    Region.Name,
                    Country.Name);
            }
        }

        #endregion

        #region Utilities

        protected override void Disclose(IDictionary<String, Object> obj,
            DisclosureLevel level,
            IDictionary<String, String> options)
        {
            base.Disclose(obj, level, options);
            if(level >= DisclosureLevel.Base)
            {
                obj["HolderID"] = HolderID;
                obj["Country"] = Country.Disclose(DisclosureLevel.Base, options);
                obj["Region"] = Region.Disclose(DisclosureLevel.Base, options);
                obj["City"] = City;
                obj["Address1"] = Address1;
                obj["Address2"] = Address2;
                obj["PostalCode"] = PostalCode;
                obj["ShortForm"] = ShortForm;
            }
            if(level >= DisclosureLevel.Extended)
            {
                obj["EntityName"] = EntityName;
                obj["ContactName"] = ContactName;
                obj["PhoneNumber"] = PhoneNumber;
                obj["FaxNumber"] = FaxNumber;
                obj["Email"] = Email;
                obj["Website"] = Website;
            }
        }

        #endregion
    }

    /// <summary>
    /// The entity NHibernate mapper.
    /// </summary>
    public class AddressMapping : EntityMapping<AddressEntity>
    {
        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public AddressMapping()
        {
            Table("fwk_Addresses");
            Id(x => x.EntityID, m =>
            {
                m.Column("AddressID");
                m.Generator(Generators.Identity);
            });
            Discriminator(x =>
            {
                x.Column("Discriminator");
                x.Length(10);
                x.Force(true);
                x.NotNullable(true);
            });
            Property(x => x.HolderID, m =>
            {
                m.NotNullable(true);
            });
            ManyToOne(x => x.Country, m =>
            {
                m.Column("CountryID");
                m.ForeignKey("FK_fwk_Addresses_fwk_Countries");
                m.NotNullable(true);
                m.Fetch(FetchKind.Join);
            });
            ManyToOne(x => x.Region, m =>
            {
                m.Column("RegionID");
                m.ForeignKey("FK_fwk_Addresses_fwk_Regions");
                m.NotNullable(true);
                m.Fetch(FetchKind.Join);
            });
            Property(x => x.City, m =>
            {
                m.NotNullable(true);
                m.Length(100);
                m.Column(c =>
                {
                    c.SqlType("nvarchar(100)");
                });
            });
            Property(x => x.Address1, m =>
            {
                m.NotNullable(true);
                m.Length(100);
                m.Column(c =>
                {
                    c.SqlType("nvarchar(100)");
                });
            });
            Property(x => x.Address2, m =>
            {
                m.NotNullable(true);
                m.Length(100);
                m.Column(c =>
                {
                    c.SqlType("nvarchar(100)");
                });
            });
            Property(x => x.EntityName, m =>
            {
                m.NotNullable(true);
                m.Length(100);
                m.Column(c =>
                {
                    c.SqlType("nvarchar(100)");
                });
            });
            Property(x => x.ContactName, m =>
            {
                m.NotNullable(true);
                m.Length(100);
                m.Column(c =>
                {
                    c.SqlType("nvarchar(100)");
                });
            });
            Property(x => x.PostalCode, m =>
            {
                m.NotNullable(true);
                m.Length(100);
                m.Column(c =>
                {
                    c.SqlType("nvarchar(100)");
                });
            });
            Property(x => x.PhoneNumber, m =>
            {
                m.NotNullable(true);
                m.Length(100);
                m.Column(c =>
                {
                    c.SqlType("nvarchar(100)");
                });
            });
            Property(x => x.FaxNumber, m =>
            {
                m.NotNullable(true);
                m.Length(100);
                m.Column(c =>
                {
                    c.SqlType("nvarchar(100)");
                });
            });
            Property(x => x.Email, m =>
            {
                m.NotNullable(true);
                m.Length(100);
                m.Column(c =>
                {
                    c.SqlType("nvarchar(100)");
                });
            });
            Property(x => x.Website, m =>
            {
                m.NotNullable(true);
                m.Length(100);
                m.Column(c =>
                {
                    c.SqlType("nvarchar(100)");
                });
            });
            Property(x => x.FlagsValue, m =>
            {
                m.Column("Flags");
                m.NotNullable(true);
            });
            Property(x => x.CreatedOn, m =>
            {
                m.NotNullable(true);
            });
            Property(x => x.UpdatedOn, m =>
            {
                m.NotNullable(true);
            });
        }
    }
}
